#include <iostream>
#include <queue>
#include <pthread.h>

namespace blockqueue
{
    const int default_cap = 5;
    template<class T> 
    class BlockQueue
    {

    public:
        BlockQueue(int cap = default_cap)
            : _cap(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
            pthread_cond_init(&_is_full, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_empty);
            pthread_cond_destroy(&_is_full);
        }

        //producter 向阻塞队列中生产数据
        void push(const T& d)
        {
            LockQueue();
            if (IsFull())
            {
                ProducterWait();
            }

            _bq.push(d);
            WakeConsumer();
            UnLockQueue();
        }

        //consumer 从阻塞队列中消费数据
        void pop(T* d)
        {
            LockQueue();
            if(IsEmpty())
            {
                ConsumerWait();
            }

            *d=_bq.front();
            _bq.pop();
            WakeProducter();
            UnLockQueue();
        }

    private:
        std::queue<T> _bq; // 阻塞队列
        int _cap;          // 阻塞队列元素的上限
        pthread_mutex_t _mtx;
        pthread_cond_t _is_full;
        pthread_cond_t _is_empty;

        bool IsFull()
        {
            return _bq.size() == _cap;
        }

        bool IsEmpty()
        {
            return _bq.size() == 0;
        }

        void LockQueue()
        {
            pthread_mutex_lock(&_mtx);
        }

        void UnLockQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void ProducterWait()
        {
            pthread_cond_wait(&_is_empty,&_mtx);
        }   

        void ConsumerWait()
        {
            pthread_cond_wait(&_is_full,&_mtx);
        }     

        void WakeProducter()
        {
            pthread_cond_signal(&_is_empty);
        }

        void WakeConsumer()
        {
            pthread_cond_signal(&_is_full);
        }
    };

}